/****************************************************************************
 * Copyright (C) 2009-2010 SciTouch LLC
 *
 * This file is part of Indigo toolkit.
 *
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 3 as published by the Free Software
 * Foundation and appearing in the file LICENSE.GPL included in the
 * packaging of this file.
 *
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ***************************************************************************/

#include <jni.h>

#include "../c/dingo_c.h"

#define JNINAME(name) JNICALL Java_com_indigo_Dingo_##name

#define CHECK(stmt) if ((*env)->ExceptionOccurred(env)) { stmt; }

void setSession (JNIEnv *env, jobject obj)
{
   jclass cls = (*env)->GetObjectClass(env, obj);
   jfieldID id = (*env)->GetFieldID(env, cls, "_sid", "J");
   jlong sid = (*env)->GetLongField(env, obj, id);
   dingoSetSessionId(sid);
}

void throwException (JNIEnv *env, const char *message)
{
   jclass cls = (*env)->FindClass(env, "java/lang/Exception");

   if (cls == NULL)
      return;

   (*env)->ThrowNew(env, cls, message);
   (*env)->DeleteLocalRef(env, cls);
}

void throwDingoException (JNIEnv *env)
{
   throwException(env, dingoGetLastError());
}

JNIEXPORT jint JNINAME(versionCode) (JNIEnv *env, jobject obj)
{
   return dingoVersionCode();
}

JNIEXPORT jstring JNINAME(versionRevisionString) (JNIEnv *env, jobject obj)
{
   return (*env)->NewStringUTF(env, dingoVersionRevisionString());
}

JNIEXPORT jlong JNINAME(allocSessionId) (JNIEnv *env, jclass cls)
{
   return dingoAllocSessionId();
}

JNIEXPORT void JNINAME(releaseSessionId) (JNIEnv *env, jclass cls, jlong id)
{
   dingoReleaseSessionId(id);
}

JNIEXPORT void JNINAME(beginLog) (JNIEnv *env, jobject obj, jstring j_path)
{
   const char* path;
   setSession(env, obj);
   path = (*env)->GetStringUTFChars(env, j_path, NULL);
   dingoBeginLog(path);
   (*env)->ReleaseStringUTFChars(env, j_path, path);
}

JNIEXPORT void JNINAME(endLog) (JNIEnv *env, jobject obj)
{
   setSession(env, obj);
   dingoEndLog();
}

JNIEXPORT void JNINAME(loadMolFromFile) (JNIEnv *env, jobject obj, jstring j_path)
{
   const char* path;
   int ret;
   setSession(env, obj);
   path = (*env)->GetStringUTFChars(env, j_path, NULL);
   ret = dingoLoadMolFromFile(path);
   (*env)->ReleaseStringUTFChars(env, j_path, path);
   if (!ret)
      throwDingoException(env);
}
JNIEXPORT void JNICALL JNINAME(loadMolFromBuffer) (JNIEnv *env, jobject obj, jbyteArray jbuf)
{
   int length, ret;
   jboolean isCopy;
   jbyte* buf;
   setSession(env, obj);
   length = (*env)->GetArrayLength(env, jbuf);
   isCopy = JNI_FALSE;
   buf = (*env)->GetByteArrayElements(env, jbuf, &isCopy);
   ret = dingoLoadMolFromBuffer((const byte*)buf, length);
   (*env)->ReleaseByteArrayElements(env, jbuf, buf, isCopy);
   if (!ret)
      throwDingoException(env);
}

JNIEXPORT void JNINAME(loadMolFromString) (JNIEnv *env, jobject obj, jstring j_str)
{
   const char* str;
   int ret;
   setSession(env, obj);
   str = (*env)->GetStringUTFChars(env, j_str, NULL);
   ret = dingoLoadMolFromString(str);
   (*env)->ReleaseStringUTFChars(env, j_str, str);
   if (!ret)
      throwDingoException(env);
}

JNIEXPORT void JNINAME(loadReactionFromFile) (JNIEnv *env, jobject obj, jstring j_path)
{
   const char* path;
   int ret;
   setSession(env, obj);
   path = (*env)->GetStringUTFChars(env, j_path, NULL);
   ret = dingoLoadReactionFromFile(path);
   (*env)->ReleaseStringUTFChars(env, j_path, path);
   if (!ret)
      throwDingoException(env);
}

JNIEXPORT void JNINAME(loadReactionFromBuffer) (JNIEnv *env, jobject obj, jbyteArray jbuf)
{
   int length, ret;
   jboolean isCopy;
   jbyte* buf;
   setSession(env, obj);
   length = (*env)->GetArrayLength(env, jbuf);
   isCopy = JNI_FALSE;
   buf = (*env)->GetByteArrayElements(env, jbuf, &isCopy);
   ret = dingoLoadReactionFromBuffer((const byte*)buf, length);
   (*env)->ReleaseByteArrayElements(env, jbuf, buf, isCopy);
   if (!ret)
      throwDingoException(env);
}

JNIEXPORT void JNINAME(loadReactionFromString) (JNIEnv *env, jobject obj, jstring j_str)
{
   const char* str;
   int ret;
   setSession(env, obj);
   str = (*env)->GetStringUTFChars(env, j_str, NULL);
   ret = dingoLoadReactionFromString(str);
   (*env)->ReleaseStringUTFChars(env, j_str, str);
   if (!ret)
      throwDingoException(env);
}

JNIEXPORT jboolean JNINAME(moleculeIsEmpty) (JNIEnv *env, jobject obj)
{
   setSession(env, obj);
   return dingoMoleculeIsEmpty() != 0;
}

JNIEXPORT jboolean JNINAME(reactionIsEmpty) (JNIEnv *env, jobject obj)
{
   setSession(env, obj);
   return dingoReactionIsEmpty() != 0;
}

JNIEXPORT void JNINAME(setOutputFormat) (JNIEnv *env, jobject obj, jstring j_format)
{
   const char* format;
   int ret;
   setSession(env, obj);
   format = (*env)->GetStringUTFChars(env, j_format, NULL);
   ret = dingoSetOutputFormat(format);
   (*env)->ReleaseStringUTFChars(env, j_format, format);
   if (!ret)
      throwDingoException(env);
}

JNIEXPORT void JNINAME(setOutputFile) (JNIEnv *env, jobject obj, jstring j_filename)
{
   const char* filename;
   int ret;
   setSession(env, obj);
   filename = (*env)->GetStringUTFChars(env, j_filename, NULL);
   ret = dingoSetOutputFile(filename);
   (*env)->ReleaseStringUTFChars(env, j_filename, filename);
   if (!ret)
      throwDingoException(env);
}

JNIEXPORT void JNINAME(setImageSize) (JNIEnv *env, jobject obj, jint w, jint h)
{
   setSession(env, obj);
   if (!dingoSetImageSize(w, h))
      throwDingoException(env);
}

JNIEXPORT void JNINAME(setBondLength) (JNIEnv *env, jobject obj, jfloat len)
{
   setSession(env, obj);
   if (!dingoSetBondLength(len))
      throwDingoException(env);
}

JNIEXPORT void JNINAME(setOptions) (JNIEnv *env, jobject obj, jstring j_opt)
{
   const char* opt;
   int ret;
   setSession(env, obj);
   opt = (*env)->GetStringUTFChars(env, j_opt, NULL);
   ret = dingoSetOptions(opt);
   (*env)->ReleaseStringUTFChars(env, j_opt, opt);
   if (!ret)
      throwDingoException(env);
}

JNIEXPORT void JNINAME(setOption) (JNIEnv *env, jobject obj, jstring j_name, jstring j_value)
{
   const char* name, *value;
   int ret;
   setSession(env, obj);
   name = (*env)->GetStringUTFChars(env, j_name, NULL);
   value = (*env)->GetStringUTFChars(env, j_value, NULL);
   ret = dingoSetOption(name, value);
   (*env)->ReleaseStringUTFChars(env, j_name, name);
   (*env)->ReleaseStringUTFChars(env, j_value, value);
   if (!ret)
      throwDingoException(env);
}

JNIEXPORT void JNINAME(setOptionEnabled) (JNIEnv *env, jobject obj, jstring j_name, jint j_value)
{
   const char* name;
   int ret;
   setSession(env, obj);
   name = (*env)->GetStringUTFChars(env, j_name, NULL);
   ret = dingoSetOptionEnabled(name, j_value);
   (*env)->ReleaseStringUTFChars(env, j_name, name);
   if (!ret)
      throwDingoException(env);
}

JNIEXPORT void JNINAME(setOptionFloat) (JNIEnv *env, jobject obj, jstring j_name, jfloat j_value)
{
   const char* name;
   int ret;
   setSession(env, obj);
   name = (*env)->GetStringUTFChars(env, j_name, NULL);
   ret = dingoSetOptionFloat(name, j_value);
   (*env)->ReleaseStringUTFChars(env, j_name, name);
   if (!ret)
      throwDingoException(env);
}

JNIEXPORT void JNINAME(setOptionColor) (JNIEnv *env, jobject obj, jstring j_name, jfloat r, jfloat g, jfloat b)
{
   const char* name;
   int ret;
   setSession(env, obj);
   name = (*env)->GetStringUTFChars(env, j_name, NULL);
   ret = dingoSetOptionColor(name, r, g, b);
   (*env)->ReleaseStringUTFChars(env, j_name, name);
   if (!ret)
      throwDingoException(env);
}

JNIEXPORT void JNINAME(setOptionSize) (JNIEnv *env, jobject obj, jstring j_name, jint w, jint h)
{
   const char* name;
   int ret;
   setSession(env, obj);
   name = (*env)->GetStringUTFChars(env, j_name, NULL);
   ret = dingoSetOptionSize(name, w, h);
   (*env)->ReleaseStringUTFChars(env, j_name, name);
   if (!ret)
      throwDingoException(env);
}

JNIEXPORT void JNINAME(render) (JNIEnv *env, jobject obj)
{
   setSession(env, obj);
   if (dingoRender() == 0)
      throwDingoException(env);
}

JNIEXPORT void JNINAME(layout) (JNIEnv *env, jobject obj)
{
   setSession(env, obj);
   if (!dingoLayout())
      throwDingoException(env);
}

JNIEXPORT void JNINAME(reset) (JNIEnv *env, jobject obj)
{
   setSession(env, obj);
   if (!dingoReset())
      throwDingoException(env);
}

JNIEXPORT jbyteArray JNINAME(getResult) (JNIEnv *env, jobject obj)
{
   byte* buf;
   int size;
   int ret;
   jbyteArray jbuf;
   setSession(env, obj);
   ret = dingoGetResult(&buf, &size);
   if (!ret)
      throwDingoException(env);
   jbuf = (*env)->NewByteArray(env, size);
   (*env)->SetByteArrayRegion(env, jbuf, 0, size, buf);
   return jbuf;
}
